from django.shortcuts import render, redirect
from django.http import HttpResponse, JsonResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from django.views import View
from .models import User, Category, Course, Flows, Codes, Orders
import json
import requests
# 导入图片库
# 绘画库
# from PIL import ImageDrawh
from PIL import ImageDraw
# 字体库
from PIL import ImageFont
# 图片库
from PIL import Image
# 随即库
import random
# 文件流
import io

# 导入上传文件夹配置
from day01.settings import UPLOAD_ROOT
import os
# 导入加密库
import hashlib
# from django.core.serializers import Serializer

from .serializer import *

# 导入原生sql语句
from django.db import connection

# 导入
import jwt

# 导入redis数据库
import redis

# 导入时间模块
import time


# 建立redis链接
r = redis.Redis(host="localhost")


#导入公共目录变量
from day01.settings import BASE_DIR

#支付宝配置 公钥和私钥
#私钥
app_private_key_string = os.path.join(BASE_DIR, "keys/app_private_2048.txt")

#公钥
app_public_key_string = os.path.join(BASE_DIR, "keys/alipay_public_2048.txt")

#初始化支付宝对象
from day01.pay import AliPay

# 导包
from django.db.models import Q, F
# 导入websocket库
from dwebsocket.decorators import accept_websocket
import uuid

# 定义u一个基于websocket协议的视图

# 定义用户id容器
clients = {}

@accept_websocket
def websocket_link(request):
    # 定义一个独立的用户id
    userid = str(uuid.uuid1())
    # 循环监听
    while True:
        # 获取连接信息
        message = request.websocket.wait()
        # 如果没有链接信息，判断链接
        if not message:
            break
        else:
            print('链接成功' + str(message, encoding="utf-8"))
            # 针对指定用户。建立连接
            clients[userid] = request.websocket

# 定义websocket发送消息
def websocket_send(request):
    # 获取消息内容
    msg = request.GET.get("msg")
    # 遍历客户端列表，推送消息
    for client in clients:
        # 推送
        clients[client].send(msg.encode('utf-8'))

    return HttpResponse({"msg":"推送消息成功"})


# 获取在线人数接口
class GetOnLine(APIView):
    def get(self, request):
        #获取客户端ip
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META.get('HTTP_X_FORWARDED_FOR')
        else:
            ip = request.META.get('REMOTE_ADDR')

        # 对用户ip进行存储
        r.sadd("onlines", ip)
        # 设置超时时间
        r.expire("onlines",20)
        # 获取在线人数
        myset = r.smembers("onlines")
        # 返回人数
        res = {}
        res['onlines'] = len(myset)

        return Response(res)


# 个人主页接口
class GetUser(APIView):

    def get(self, request):

        #获取用户id
        uid = request.GET.get('uid', '')

        #获取客户端ip
        if 'HTTP_X_FORWARDED_FOR' in request.META:
            ip = request.META.get('HTTP_X_FORWARDED_FOR')
        else:
            ip = request.META.get('REMOTE_ADDR')

        #设置防刷逻辑
        if r.get(ip) == None:
            #将用户访问数累加
            User.objects.filter(Q(id=int(uid))).update(num=F("num")+1)
            r.set(ip, ip)
            #设置访问周期为60秒
            r.expire(ip, 60)
        else:
            pass

        print(r.get(ip))
        #查询用户
        user = User.objects.filter(Q(id=int(uid))).first()

        #序列化
        user_ser = UserModelSer(user)

        return Response(user_ser.data)


def get_ali_object():
    #定义appid
    appid = "2016092600603658"
    #定义
    return_url = "http://localhost:8000/ali_back/"
    #实例化对象
    alipay = AliPay(
        appid=appid,  # 应用id
        app_notify_url=return_url,  # 响应网址
        return_url=return_url,  # 回调网址
        app_private_key_path=app_private_key_string,  # 私钥地址
        alipay_public_key_path=app_public_key_string,  # 公钥地址
        debug=True  # 默认开启测试模式
    )

    #返回实例
    return alipay


#构造支付url
def payurl(request):
    #支付价格
    price = request.GET.get('price', '')
    #订单号
    orderid = request.GET.get('orderid', '')

    #生成实例
    alipay = get_ali_object()

    #格式化参数
    params = alipay.direct_pay(
        subject="testpay",  # 商品描述
        out_trade_no=str(orderid),  # 订单号
        total_amount=price  # 交易金额
    )

    #拼接支付url 沙盒环境
    pay_url = "https://openapi.alipaydev.com/gateway.do?{0}".format(params)

    return redirect(pay_url)


#定义支付回调方法
def ali_pay_back(request):

    #接收参数
    orderid = request.GET.get("out_trade_no")

    print(orderid)

    #修改订单状态
    orders = Orders.objects.get(orderid=orderid)
    orders.status = 1
    orders.save()

    #重定向到订单列表页

    return redirect("http://localhost:8080/order")


#定义订单号生成方法
def get_order_code():
    order_no = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
    return order_no


#定义订单列表页
#课程检索逻辑
class OrdersList(APIView):
    def get(self, request):
        print(app_private_key_string)

        #获取参数
        uid = request.GET.get('uid')

        #获取订单列表
        orderslist = Orders.objects.filter(uid=int(uid))

        #序列化
        orderslist_ser = OrdersModelSer(orderslist, many=True)

        #返回数据
        return Response(orderslist_ser.data)


#订单入库
class OrdersInsert(APIView):

    def get(self, request):
        #接收参数
        uid = request.GET.get('uid', '')
        price = request.GET.get('price', '')
        # 签名
        sign = request.GET.get('sign', '')
        #课程明细字段
        cids = request.GET.get('cids', '')

        # 后台验证签名(验签)
        # 生成md5对象
        md5 = hashlib.md5()
        # 定义加密对象
        sign_str = "mysign" + "price" +price
        # 转码
        sign_utf8 = str(sign_str).encode(encoding="utf-8")
        # 加密
        md5.update(sign_utf8)
        # 生成密文
        md5_server = md5.hexdigest()

        # 比对签名
        if md5_server != sign:
            res = {}
            res['code'] = 401
            res['message'] = '您篡改了课程价格'
            return Response(res)

        print(md5_server)

        #生成订单号
        orderid = get_order_code()

        #进行入库逻辑
        orders = Orders(uid=int(uid), price=int(price), orderid=orderid,cids=cids)
        #保存
        orders.save()

        #返回结果
        res = {}
        res['code'] = orderid
        res['message'] = '订单创建成功'
        return Response(res)


#优惠卷接口
class SingleCode(APIView):
    def get(self, request):

        #获取参数
        code = request.GET.get('code')
        #获取单课程
        codes = Codes.objects.filter(code=code).first()

        print(codes.status)

        #判断优惠卷状态
        if codes.status == 0:
            res = {}
            res['code'] = 401
            res['message'] = '该优惠卷已经过期'
            return Response(res)

        #序列化
        codes_ser = CodesModelSer(codes)

        codes.status = 0
        codes.save()

        #返回结果
        res = {}
        res['code'] = 200
        res['data'] = codes_ser.data
        return Response(res)


# 自定义装饰器
def my_de():
    def decorator(view_func):
        def _wrapped_view(request, *args, **kwargs):
            # 判断用户合法性
            # 获取用户id
            uid = request.GET.get('id')
            # 获取token
            token = request.GET.get('token')

            # 解码
            # 捕获异常
            try:
                de_token = jwt.decode(token, '1314', algorithms=['HS256'])
            except Exception as e:
                res = {}
                res['message'] = '您是非买用户'
                return HttpResponse(json.dumps(res, ensure_ascii=False), content_type='application/json')

            # 判断
            if uid != de_token['uid']:
                res = {}
                res['message'] = '您是非买用户'
                return HttpResponse(json.dumps(res, ensure_ascii=False), content_type='application/json')

            print(de_token)

            return view_func(request, *args, **kwargs)
        return _wrapped_view
    return decorator


# sql格式化方法
def dictfetchall(cursor):
    # 获取游标描述
    desc = cursor.description
    return [
        dict(zip([col[0] for col in desc], row))

        for row in cursor.fetchall()
    ]


# 获取当前用户关注课程列表
@my_de()
def get_myflow(request):

    uid = request.GET.get('id')

    #建立游标对象
    cursor = connection.cursor()
    #连表查询
    cursor.execute(
        'select a.username,c.title,c.id from user a left join flows b on a.id = b.uid left join course c on b.cid = c.id where a.id = %s ' % uid)
    #获取结果集
    result = dictfetchall(cursor)
    print(result)
    #返回结果
    return HttpResponse(json.dumps(result, ensure_ascii=False), content_type='application/json')


# 课程排行榜数据
def myrank(request):
    # 初始化数据
    # r.zadd('myrank_edu', {'2': 0})
    # r.zadd('myrank_edu', {'3': 0})
    # r.zadd('myrank_edu', {'4': 0})
    # r.zadd('myrank_edu', {'5': 0})

    mylist = r.zrange('myrank_edu', 0, 10, desc=True, withscores=True)

    # 遍历数据
    datalist = []
    for x in mylist:
        res = {}
        # 强转数据类型
        res['id'] = str(x[0], 'utf-8')
        res['flow'] = int(x[1])
        datalist.append(res)
  
    print(datalist)

    return HttpResponse('ok')


# 课程检索逻辑
class CourseSearch(APIView):

    def get(self, request):
        # 获取参数
        title = request.GET.get('title')
        print(title)
        # 获取课程列表
        # courselist = Course.objects.filter(title=title)
        # where title like
        courselist = Course.objects.filter(title__contains=title)

        # 序列化
        courselist_ser = CouModelSer(courselist, many=True)

        # 返回数据
        return Response(courselist_ser.data)


# 获取关注状态
class GetFlow(APIView):
    def get(self, request):
        # 获取参数
        uid = request.GET.get('uid')
        cid = request.GET.get('cid')
        # 获取单关注
        flows = Flows.objects.filter(uid=int(uid), cid=int(cid)).first()
        # 判断结果
        if flows:
            ret = {}
            ret['code'] = 200
            ret['status'] = 1
        else:
            ret = {}
            ret['code'] = 200
            ret['status'] = 0

        # 返回结果
        return Response(ret)


# 关注入库
class FlowsInsert(APIView):
    def get(self, request):
        # 接收参数
        uid = request.GET.get('uid', '')
        cid = request.GET.get('cid', '')
        myflow = request.GET.get('myflow', '1')

        # 取消关注
        if myflow == "0":
            Flows.objects.filter(uid=uid, cid=cid).delete()

            # 修改课程的总关注数
            course = Course.objects.get(id=int(cid))
            course.flow = course.flow - 1
            course.save()

            res = {}
            res['code'] = 200
            res['message'] = '取消关注成功'
            return Response(res)

        # 排重操作
        flows = Flows.objects.filter(uid=uid, cid=cid).first()

        if flows:
            res = {}
            res['code'] = 405
            res['message'] = '已经关注过课程'
            return Response(res)

        # 进行入库逻辑
        flows = Flows(uid=int(uid), cid=int(cid))
        # 保存
        flows.save()
        # 修改课程的总关注数
        course = Course.objects.get(id= int(cid))
        course.flow = course.flow + 1
        course.save()

        # 返回结果
        res = {}
        res['code'] = 200
        res['message'] = '关注成功'
        return Response(res)


# 反序列化入库   评论入库
class CommentInsert(APIView):
    def post(self, request):
        # 初始化参数
        comment = CommModelSer(data=request.data)
        # 验证字段正确性
        if comment.is_valid():
            comment.create(comment.data)

        # 返回结果
        res = {}
        res['code'] = 200
        res['message'] = '评论成功'

        return Response(res)


# 评论列表
class CommentList(APIView):

    def get(self, request):
        # 接收参数
        cid = request.GET.get("id")
        # 读取数据
        commentlist = Comment.objects.filter(cid=cid)
        # 序列化
        commentlist_ser = CommModelSer(commentlist, many=True)
        # 返回结果
        return Response(commentlist_ser.data)


# 加密方法
def make_password(mypass):
    # md5算法
    mypass = hashlib.md5()
    # 字符串进行编译(加盐操作)
    mypass.update('521'.encode(encoding='utf-8'))
    # 返回密文
    return mypass.hexdigest()


# 单课程接口
class SingleCourse(APIView):

    def get(self, request):
        # 获取参数
        id = request.GET.get('id')
        # 获取课程列表
        course = Course.objects.filter(id=int(id)).first()
        # 序列化
        course_ser = CouModelSer(course)
        # 返回结果
        return Response(course_ser.data)


# 课程列表
class CourseList(APIView):

    def get(self, request):
        # 当前页
        page = request.GET.get('page', 1)
        # 每页条数
        size = request.GET.get('size', 1)
        # 开始位置
        data_start = (int(page) - 1) * int(size)
        # 结束位置
        data_end = int(page) * int(size)

        # 获取课程列表
        clist = Course.objects.all()[data_start:data_end]
        # 获取总条数
        count = Course.objects.count()

        # 序列化
        clist_ser = CouModelSer(clist, many=True)
        # 返回容器
        res = {}
        res['total'] = count
        res['data'] = clist_ser.data

        # 返回结果
        return Response(res)


# 课程入库接口
class AddCourse(APIView):

    def get(self, request):
        # 接收参数
        title = request.GET.get('title', '')
        desc = request.GET.get('desc', '')
        category = request.GET.get('category', '')
        img = request.GET.get('img', '')
        uid = request.GET.get('uid', '')
        price = request.GET.get('price', '')
        # 查询分类
        cate = Category.objects.filter(name=category).first()

        # 排重操作
        course = Course.objects.filter(title=title).first()

        if course:
            res = {}
            res['code'] = 405
            res['message'] = '课程已经存在'
            print(type(res))
            return Response(res)

        # 进行入库逻辑
        cours = Course(title=title, desc=desc, price=int(price), cid=int(cate.id), uid=int(uid), img=img)

        # 保存
        cours.save()

        # 返回结果
        res = {}
        res['code'] = 200
        res['message'] = '添加课程成功'

        return Response(res)


# 定义课程分类接口
class CategoryList(APIView):

    def get(self, request):
        cates = Category.objects.all()
        # 注instance用于接受序列化的对象，many表示是queryset对象
        cates_ser = CatModelSer(cates, many=True)
        return Response(cates_ser.data)


# 添加课程分类
class AddCategory(APIView):

    def post(self, request):
        cates = CatModelSer(data=request.data)
        print(cates)
        if cates.is_valid():
            cates.save()
            return Response({
                'code': 200,
                'message': 'ok'
            })
        print(cates.errors)
        return Response({
            'code': 400,
            'message': 'off'
        })


# 定义文件上传类
class UploadFile(View):
    def post(self, request):
        img = request.FILES.get('file')
        # 建立文件流对象
        f = open(os.path.join(UPLOAD_ROOT, '', img.name), 'wb')
        # with open(os.path.join(UPLOAD_ROOT, '', img.name, 'wb')) as f:

        # 写文件 遍历文件流
        for chunk in img.chunks():
            f.write(chunk)
        f.close()

        # 返回结果
        return HttpResponse(json.dumps({'filename': img.name}), content_type='application/json')


# 定义验证码类
class MyCode(View):
    # 定义随机取色方法
    def get_random_color(self):
        R = random.randrange(255)
        G = random.randrange(255)
        B = random.randrange(255)
        return (R, G, B)

    def get(self, request):
        # 定义画布
        img_size = (120, 50)
        # 定义图像 颜色种类 画布 背景颜色
        image = Image.new('RGB', img_size, 'white')
        # 定义画笔
        draw = ImageDraw.Draw(image, 'RGB')
        # 随机字符串组成
        source = '01234567890asdfghor'
        # 定义容器
        code_str = ''
        for i in range(4):
            # 取色
            text_color = self.get_random_color()
            # 获取一个字符串
            tmp_num = random.randrange(len(source))
            # 获取字符集
            random_str = source[tmp_num]
            # 将随机生成的字符串放入定义好的容器
            code_str += random_str
            # 将文字输入绘图
            draw.text((10 + 30 * i, 20), random_str, text_color)
        # 建立 io 文件流
        buf = io.BytesIO()
        # 保存内存中
        image.save(buf, 'png')
        # 将验证码放入session
        request.session['code'] = code_str
        print(request.session['code'])

        # 测试redis
        import redis
        # 建立链接
        r = redis.Redis('localhost')

        r.set('mykey', code_str)

        # 返回文件文件流图像
        return HttpResponse(buf.getvalue(), 'image/png')


# 定义回调方法
def weibo_back(request):
    # 获取新浪微博回调的code
    code = request.GET.get('code')
    # 微博认证地址
    access_token_url = "https://api.weibo.com/oauth2/access_token"
    # 发起 http 请求
    re_dict = requests.post(
        access_token_url,
        data={
            # app_id 应用id
            "client_id": "2652972383",
            # app_secret 密钥
            "client_secret": "fbf14c4f1bee1e362f4c8c38514a25cd",
            # 授权类型 code
            "grant_type": "authorization_code",
            # code 参数
            "code": code,
            # 回调网址
            "redirect_uri": "http://127.0.0.1:8000/index/"
        }
    )
    # 打印数据
    re_dict = re_dict.text
    # 强转数据类型
    re_dict = eval(re_dict)

    # 判断是否用新浪微博账号登录
    user = User.objects.filter(username=str(re_dict['uid'])).first()

    sina_id = ''
    user_id = ''
    if user:
        # 代表曾经登陆并且生成过账号
        sina_id = user.username
        user_id = user.id
    else:
        # 自动创建账号
        user = User(username=str(re_dict['uid']), password=make_password(''), type=0)
        # 保存
        user.save()
        sina_id = str(re_dict['uid'])
        # 查询数据库的id
        user = User.objects.filter(username=str(re_dict['uid'])).first()
        user_id = user.id

    # 增加token密钥
    encode_jwt = jwt.encode({'uid': user_id}, '1314', algorithm='HS256')
    # 转码
    encode_str = str(encode_jwt, 'utf-8')
    print(encode_str)

    # 进行跳转
    return redirect("http://localhost:8080?sina_id=" + sina_id + "&uid=" + str(user_id)+"&token"+encode_str)
    # return HttpResponse("test")


# 登录
class Login(APIView):
    def post(self, request):
        # 接受参数
        username = request.POST.get('username', '未收到用户名')
        password = request.POST.get('password', '未收到密码')
        # 查询数据
        user = User.objects.filter(username=username, password=make_password(password)).first()
        print(type(user))
        res = {}
        if user:
            # 返回结果
            res['code'] = 200
            res['message'] = '登录成功'
            res['username'] = user.username
            print(res)
            return Response(res)
        else:
            res['code'] = 405
            res['message'] = '您的用户或密码错误'
            return Response(res)


# 注册
class Register(APIView):
    def get(self, request):
        # 接受参数
        username = request.GET.get('username', '未收到用户名')
        password = request.GET.get('password', '未收到密码')
        password2 = request.GET.get('password2', '未收到密码')
        # 获取随机验证码
        code = request.GET.get('code', '为获取验证码')

        # 测试redis
        import redis
        # 建立链接
        r = redis.Redis('localhost')

        mycode = r.get('mykey')
        # 转码
        mycode = mycode.decode('utf-8')
        print(mycode)

        # 获取sessio 中的验证码
        # mycode = request.session.get('code', None)
        # print(mycode)

        res = {}
        if code != mycode:
            res['code'] = 405
            res['message'] = '验证码输入错误'
            return Response(res)

        # 判断两次密码是否相同
        if password != password2:
            res['message'] = '两次密码不一致'
            return Response(res)
        else:
            # 排重操作
            user = User.objects.filter(username=username).first()

            if user:
                res['code'] = 405
                res['message'] = '用户已存在'

                return Response(res)

        # 进行入库逻辑 加密
        user = User(username=username, password=make_password(password), type=0)

        # 保存
        user.save()

        # 返回结果

        res['code'] = 200
        res['message'] = '注册成功'
        return Response(res)

# def mytem(request):
#     if request.method == 'GET':
#         res = User.objects.all()
#         return render(request, 'index.html', locals())
#     if request.method == 'POST':
#         username = request.POST.get('username')
#         print(username)
#         res = User.objects.filter(username__icontains=username)
#         return render(request, 'index.html', locals())


# def appends(request):
#     if request.method == 'GET':
#         return render(request, 'appends.html', locals())
#     if request.method == 'POST':
#         username = request.POST.get('username')
#         password = request.POST.get('password')
#         img = request.FILES.get('img')
#         type = request.POST.get('type')
#         if not all:
#             return HttpResponse('不能为空')
#         else:
#             if img:
#                 imgs = os.path.join(UPLOAD_ROOT[0], 'static/upload/' + img.name)
#                 with open(imgs, 'wb') as f:
#                     f.write(img.read())
#                     User.objects.create(
#                         username=username,
#                         password=password,
#                         image='static/upload/' + img.name,
#                         type=type
#                     )
#                 return redirect('/')
#         return HttpResponse('5555')


# def updat(request):
#     if request.method == 'GET':
#         return render(request, 'updat.html', locals())
#     if request.method == 'POST':
#         id = request.GET.get('id')
#         username = request.POST.get('username')
#         password = request.POST.get('password')
#         img = request.FILES.get('img')
#         type = request.POST.get('type')
#         if not all:
#             return HttpResponse('...')
#         else:
#             if img:
#                 imgs = os.path.join(UPLOAD_ROOT[0], 'static/upload/' + img.name)
#                 with open(imgs, 'wb') as f:
#                     f.write(img.read())
#                     User.objects.filter(id=id).update(username=username, password=password, image='static/upload/' + img.name,
#                                                       type=type)
#                 return redirect('/')
#         return HttpResponse('6666666')


# def dels(request):
#     if request.method == 'GET':
#         id = request.GET.get('id')
#         User.objects.filter(id=id).delete()
#         return redirect('/')
